/*********************************************************************************
 *      Copyright:  (C) 2021 iot
 *                  All rights reserved.
 *
 *       Filename:  util_proc.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0
 *         Author:  mk
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>

#include "util_proc.h"
#include "logger.h"

proc_signal_t     g_signal={0};

void proc_default_sighandler(int sig)
{
    switch(sig)
    {
        case SIGINT:  
            log_warn("SIGINT - stopping\n");
            g_signal.stop = 1;
            break;

        case SIGTERM:  
            log_warn("SIGTERM - stopping\n");
            g_signal.stop = 1;
            break;

        case SIGSEGV:  
            log_warn("SIGSEGV - stopping\n");
            break;

        case SIGPIPE:
            log_warn("SIGPIPE - warnning\n");
            break;

        default:
            break;
    }
}

/*  install default signal process functions     */
void install_default_signal(void)
{
    struct sigaction sigact, sigign;

    log_nrml("Install default signal handler.\n");

     /*   Initialize the catch signal structure.  */
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigact.sa_handler = proc_default_sighandler;

    /*   Setup the ignore signal.  */
    sigemptyset(&sigign.sa_mask);
    sigign.sa_flags = 0;
    sigign.sa_handler = SIG_IGN;

    sigaction(SIGTERM, &sigact, 0); /*   catch terminate signal "kill" command */
    sigaction(SIGINT,  &sigact, 0); /*   catch interrupt signal CTRL+C */
    sigaction(SIGSEGV, &sigact, 0); /*   catch segmentation faults  */
    sigaction(SIGPIPE, &sigact, 0); /*   catch broken pipe */ 
}

/*  ****************************************************************************
 * FunctionName: record_daemon_pid
 * Description : Record the running daemon program PID to the file "pid_file"
 * Inputs      : pid_file:The record PID file path
 * Output      : NONE
 * Return      : 0: Record successfully  Else: Failure
 * *****************************************************************************/
int record_daemon_pid(const char *pid_file)
{
    struct stat fStatBuf;
    int fd = -1;
    int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU;
    char ipc_dir[64] = { 0 };

    strncpy(ipc_dir, pid_file, 64);

    /*  dirname() will modify ipc_dir and save the result */
    dirname(ipc_dir);

    /*  If folder pid_file PATH doesnot exist, then we will create it" */
    if (stat(ipc_dir, &fStatBuf) < 0)
    {
        if (mkdir(ipc_dir, mode) < 0)
        {
            log_fatal("cannot create %s: %s\n", ipc_dir, strerror(errno));
            return -1;
        }

        (void)chmod(ipc_dir, mode);
    }

    /*   Create the process running PID file */
    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    if ((fd = open(pid_file, O_RDWR | O_CREAT | O_TRUNC, mode)) >= 0)
    {
        char pid[PID_ASCII_SIZE];
        snprintf(pid, sizeof(pid), "%u\n", (unsigned)getpid());
        write(fd, pid, strlen(pid));
        close(fd);

        log_dbg("Record PID<%u> to file %s.\n", getpid(), pid_file);
    }
    else
    {
        log_fatal("cannot create %s: %s\n", pid_file, strerror(errno));
        return -1;
    }

    return 0;
}


/*  ****************************************************************************
 * FunctionName: get_daemon_pid
 * Description : Get the daemon process PID from the PID record file "pid_file"
 * Inputs      : pid_file: the PID record file
 * Output      : NONE
 * Return      : pid_t: The daemon process PID number
 * *****************************************************************************/
pid_t get_daemon_pid(const char *pid_file)
{
    FILE *f;
    pid_t pid;

    if ((f = fopen(pid_file, "rb")) != NULL)
    {
        char pid_ascii[PID_ASCII_SIZE];
        (void)fgets(pid_ascii, PID_ASCII_SIZE, f);
        (void)fclose(f);
        pid = atoi(pid_ascii);
    }
    else
    {
        log_fatal("Can't open PID record file %s: %s\n", pid_file, strerror(errno));
        return -1;
    }
    return pid;
}


/*  ****************************************************************************
 * FunctionName: check_daemon_running
 * Description : Check the daemon program already running or not
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 1: The daemon program alread running   0: Not running
 * *****************************************************************************/
int check_daemon_running(const char *pid_file)
{
    int retVal = -1;
    struct stat fStatBuf;

    retVal = stat(pid_file, &fStatBuf);
    if (0 == retVal)
    {
        pid_t pid = -1;
        printf("PID record file \"%s\" exist.\n", pid_file);

        pid = get_daemon_pid(pid_file);
        if (pid > 0)  /*   Process pid exist */
        {
            if ((retVal = kill(pid, 0)) == 0)
            {
                printf("Program with PID[%d] seems running.\n", pid);
                return 1;
            }
            else   /*  Send signal to the old process get no reply. */
            {
                printf("Program with PID[%d] seems exit.\n", pid);
                remove(pid_file);
                return 0;
            }
        }
        else if (0 == pid)
        {
            printf("Can not read program PID form record file.\n");
            remove(pid_file);
            return 0;
        }
        else  /*  Read pid from file "pid_file" failure */
        {
            printf("Read record file \"%s\" failure, maybe program still running.\n", pid_file);
            return 1;
        }
    }

    return 0;
}


/*  ****************************************************************************
 * FunctionName: set_daemon_running
 * Description : Set the programe running as daemon if it's not running and record 
 *               its PID to the pid_file.
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 0: Successfully. 1: Failure
 * *****************************************************************************/
int set_daemon_running(const char *pid_file)
{
    daemon(0, 1);
    log_nrml("Program running as daemon [PID:%d].\n", getpid());

    if (record_daemon_pid(pid_file) < 0)
    {
        log_fatal("Record PID to file \"%s\" failure.\n", pid_file);
        return -2;
    }

    return 0;
}


/*  ****************************************************************************
 * FunctionName: stop_daemon_running
 * Description : Stop the daemon program running
 * Inputs      : pid_file: The record running daemon program PID
 * Output      : NONE
 * Return      : 1: The daemon program alread running   0: Not running
 * *****************************************************************************/
int stop_daemon_running(const char *pid_file)
{
    pid_t            pid = -1;
    struct stat      fStatBuf;

    if ( stat(pid_file, &fStatBuf) < 0)
        return 0;

    printf("PID record file \"%s\" exist.\n", pid_file);
    pid = get_daemon_pid(pid_file);
    if (pid > 0)  /*   Process pid exist */
    {
        while ( (kill(pid, 0) ) == 0)
        {
            kill(pid, SIGTERM);
            sleep(1);
        }

        remove(pid_file);
    }

    return 0;
}


/*  start a new thread to run $thread_workbody point function   */
int thread_start(pthread_t *thread_pid, thread_body_t thread_workbody, void *thread_arg)
{
    pthread_attr_t      thread_attr;

     /*  Initialize the thread  attribute  */
    if( pthread_attr_init(&thread_attr) )
    {
        return -1;
    }

    /*  Set the stack size of the thread */
    if( pthread_attr_setstacksize(&thread_attr, 120*1024) )
    {
        goto CleanUp;
    }

     /*  Set thread to detached state:Don`t need pthread_join  */
    if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )
    {
        goto CleanUp;
    }

    /*  Create the thread */
    if( pthread_create(thread_pid, &thread_attr, thread_workbody, thread_arg) )
    {
        goto CleanUp;

    }

CleanUp:
    /*  Destroy the  attributes  of  thread */
    pthread_attr_destroy(&thread_attr);

    return 0;
}



